Completed
Pull Request — master (#99)
by
unknown
01:14
created

BtccomConverter.convertBlockTx   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
c 2
b 0
f 0
nc 1
dl 0
loc 15
rs 9.4285
nop 1

1 Function

Rating   Name   Duplication   Size   Complexity  
A 0 5 1
1
var RestClient = require('./rest_client');
2
var Wallet = require('./wallet');
3
var bitcoinJS = require('bitcoinjs-lib');
4
5
var BtccomConverter = function(network, useNewCashAddr) {
6
    this.network = network;
7
    this.useNewCashAddr = useNewCashAddr;
8
9
    this.specialRawTxClientOnly = new RestClient({
10
        host: "btc.com",
11
        https: true,
12
        port: 443,
13
        endpoint: "/"
14
    });
15
    this.btccomApiClient = new RestClient({
16
        host: "chain.api.btc.com",
17
        https: true,
18
        port: 443,
19
        endpoint: "/v3"
20
    });
21
};
22
23
function getAddressScriptInfo(address, network, useNewCashAddr) {
24
    try {
25
        var addressScriptInfo = bitcoinJS.address.toOutputScript(address, network, useNewCashAddr);
26
    } catch (e) {
27
        addressScriptInfo = null;
28
    }
29
    return addressScriptInfo;
30
}
31
32
function getScriptAsm(chunks) {
33
34
    try {
35
        var scriptAsm = bitcoinJS.script.toASM(chunks);
36
    } catch (e) {
37
        scriptAsm = null;
38
    }
39
    return scriptAsm;
40
}
41
42
function getType(script) {
43
    try {
44
        var type = bitcoinJS.script.classifyOutput(script);
45
    } catch (e) {
46
        type = null;
47
    }
48
    return type;
49
}
50
51
function getBase58AddressHash160(address, network, useNewCashAddr) {
52
    try {
53
        var addressInfo = Wallet.getAddressAndType(address, network, useNewCashAddr);
54
    } catch (e) {
55
        console.log(e);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
56
        return null;
57
    }
58
59
    if (addressInfo.type == "base58") {
60
        return addressInfo.decoded.hash;
61
    } else if (addressInfo.type == "bech32") {
62
        if (addressInfo.data.length == 20) {
63
            return addressInfo.decoded.hash;
64
        }
65
        return null;
66
    } else if (addressInfo.type == "") {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if addressInfo.type == "" is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
67
        return addressInfo.decoded.hash;
68
    }
69
}
70
71
var utcTimestampToISODateStr = function(time) {
72
    new Date
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new Date() is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
73
    return (new Date(time)).toISOString();
74
};
75
76
BtccomConverter.prototype.getUrlForBlock = function(blockHash) {
77
    return "/block/" + blockHash;
78
};
79
80
BtccomConverter.prototype.getUrlForTransaction = function(txId) {
81
    return "/tx/" + txId + "?verbose=3"
82
};
83
84
BtccomConverter.prototype.getUrlForBlockTransaction = function(blockHash) {
85
    return "/block/" + blockHash + "/tx"
86
};
87
88
BtccomConverter.prototype.getUrlForAddress = function(address) {
89
    return "/address/" + address;
90
};
91
92
BtccomConverter.prototype.getUrlForAddressTransactions = function(address) {
93
    return "/address/" + address + "/tx?verbose=3";
94
};
95
96
BtccomConverter.prototype.getUrlForAddressUnspent = function(address) {
97
    return "/address/" + address + "/unspent";
98
};
99
100
BtccomConverter.prototype.getUrlForAllblocks = function() {
101
    return "/all-blocks";
102
};
103
104
BtccomConverter.prototype.handleErros = function(self, data) {
105
    if (self.converter instanceof BtccomConverter) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if self.converter instanceof BtccomConverter is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
106
        if (data.err_no === 0 || data.data !== null) {
107
            return data;
108
        } else {
109
            return {
110
                err_no: data.err_no,
111
                err_msg: data.err_msg,
112
                data: data.data
113
            }
114
        }
115
    }
116
}
117
118
BtccomConverter.prototype.convertBlock = function(oldData) {
119
    var data = {};
120
    data.hash = oldData.data.hash;
121
    data.version = oldData.data.version;
122
    data.height = oldData.data.height;
123
    data.block_time = utcTimestampToISODateStr(oldData.data.timestamp);
124
    data.arrival_time = utcTimestampToISODateStr(oldData.data.timestamp);
125
    data.bits = oldData.data.bits;
126
    data.nonce = oldData.data.nonce;
127
    data.merkleroot = oldData.data.mrkl_root;
128
    data.prev_block = oldData.data.prev_block_hash;
129
    data.next_block = oldData.data.next_block_hash ;
130
    data.byte_size = oldData.data.size;
131
    data.difficulty = oldData.data.pool_difficulty; //todo is there a replacement? kill this off for now
132
    data.transactions = oldData.data.tx_count;
133
    data.reward_block = oldData.data.reward_block;
134
    data.reward_fees = oldData.data.reward_fees;
135
    data.created_at = oldData.data.created_at;
136
    data.confirmations = oldData.data.confirmations;
137
    data.is_orphan = oldData.data.is_orphan ;
138
    data.is_sw_block = oldData.data.is_sw_block;
139
    data.byte_size = oldData.data.stripped_size;
140
    data.weight = oldData.data.weight;
141
    data.miningpool_name = oldData.data.miningpool_name ? oldData.data.miningpool_name : null;
142
    data.miningpool_url  = oldData.data.miningpool_url ?  oldData.data.miningpool_url : null;
143
    data.miningpool_slug = oldData.data.miningpool_slug ? oldData.data.miningpool_slug : null;
144
145
    return data;
146
};
147
148
149
BtccomConverter.prototype.convertBlockTx = function(oldData) {
150
151
    var list = [];
152
    oldData.data.list.forEach(function(j1) {
153
        var j = convertBtccomTxToBlocktrail(j1);
154
155
        list.push(j);
156
    });
157
    return {
158
        data: list,
159
        current_page: oldData.data.page,
160
        per_page: oldData.data.pagesize,
161
        total: oldData.data.total_count
162
    };
163
};
164
165
function convertBtccomTxToBlocktrail(tx) {
166
    var data = {};
167
168
    data.size = tx.vsize;
169
    data.hash = tx.hash;
170
    data.block_height = tx.block_height ;
171
    data.block_time = utcTimestampToISODateStr(tx.block_time)
172
    data.block_hash = 123//todo;
173
    data.confirmations = tx.confirmations;
174
    data.is_coinbase = tx.is_coinbase;
175
176
    var totalInputValue;
177
    if (data.is_coinbase) {
178
        totalInputValue = tx.outputs[0].value - tx.fee
179
    } else {
180
        totalInputValue = tx.inputs_value;
181
    }
182
183
    data.total_input_value = totalInputValue;
184
    data.total_output_value = tx.outputs[0].value;
185
    data.total_fee = tx.fee;
186
    data.contains_dust = null; //todo is there a replacement? kill this off for now
187
    data.inputs = [];
188
    data.outputs = [];
189
    for (var inputIdx in tx.inputs) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
190
        var i = tx.inputs[inputIdx];
191
        var scriptType;
192
        var inputValue;
193
        var inputTxid;
194
        var outpointIdx;
195
196
        if (data.is_coinbase && i.prev_position == -1 && i.prev_tx_hash == "0000000000000000000000000000000000000000000000000000000000000000") {
197
            scriptType = "coinbase";
198
            inputTxid = null; // debatable.
199
            inputValue = totalInputValue;
200
            outpointIdx = 0xffffffff;
0 ignored issues
show
Unused Code introduced by
The variable outpointIdx seems to be never used. Consider removing it.
Loading history...
201
        } else {
202
            scriptType = i.prev_type;
203
            inputValue = i.prev_value;
204
            inputTxid = i.prev_tx_hash;
205
            outpointIdx = i.prev_position
206
        }
207
208
        var prevAddress = null;
209
        if (typeof i.prev_addresses === "Array") {
210
            prevAddress = i.prev_addresses;
211
        }
212
213
        data.inputs.push({
214
            output_hash: inputTxid,
215
            output_index: inputIdx,
216
            output_confirmed: false, //todo ask ruben
217
            value: inputValue,
218
            sequence: i.sequence,
219
            address: prevAddress,
220
            type: scriptType,
221
            prev_value: 0,
222
            multisig: i.multisig == undefined ? null : i.multisig,
223
            multisig_addresses: i.multisig_addresses == undefined ? null : i.multisig_addresses,
224
            script_signature: i.script_hex
225
        });
226
    }
227
228
    for (var outIdx in tx.outputs) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
229
        var i = tx.outputs[outIdx];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 190. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
230
        data.outputs.push({
231
            index: outIdx,
232
            value: i.value,
233
            address: i.addresses,
234
            type: convertBtccomOutputScriptType(i.type),
235
            script: i.script_asm,
236
            script_hex: i.script_hex,
237
            spent_hash: i.spent_by_tx,
238
            spent_index: i.spent_by_tx_position
239
        });
240
    }
241
242
    data.size = tx.size;
243
    data.is_double_spend = tx.is_double_spend;
244
    data.opt_in_rbf = false; //todo ask ruben
245
    data.unconfirmed_inputs = true; //todo ask ruben
246
    data.lock_time_timestamp = null;
247
    data.lock_time_block_height = null;
248
249
    /*
250
    Extra fields from Btc.com
251
    */
252
253
    data.is_sw_tx = tx.is_sw_tx;
254
    data.weight = tx.weight;
255
    data.witness_hash = tx.witness_hash;
256
    data.lock_time  = tx.lock_time;
257
    data.sigops = tx.sigops;
258
    data.version = tx.version;
259
260
    return data;
261
}
262
263
BtccomConverter.prototype.convertTx = function(oldData, rawTx) {
264
    var data = convertBtccomTxToBlocktrail(oldData.data);
265
    data.raw = rawTx;
266
    return data;
267
}
268
269
function convertBtccomOutputScriptType(scriptType) {
270
    switch (scriptType) {
271
        case "P2PKH_PUBKEY":
272
            return "pubkey";
273
        case "P2PKH":
274
            return "pubkeyhash";
275
        case "P2SH":
276
            return "scripthash";
277
        case "P2WSH_V0":
278
            return "witnessscripthash";
279
        case "P2WPKH":
280
            return "witnesspubkeyhash";
281
        default:
282
            throw new Error("Not implemented yet, do this later")
283
    }
284
}
285
/// self.converter.convertAddressTx(self.converter.handleResponse(data))
286
BtccomConverter.prototype.convertAddressTx = function(oldData) {
287
    var data = [];
288
289
290
    oldData.data.list.forEach(function(i) {
291
        var e  = {};
292
        var inputs = [];
293
        var outputs = [];
294
295
        e.hash = i.hash;
296
        e.time = i.block_time;
297
        e.confirmations = i.confirmations;
298
        e.block_height = i.block_height;
299
        e.block_hash = i.hash;
300
        e.is_coinbase = i.is_coinbase;
301
        e.estimated_value = i.estimated_value; //todo ask ruben
302
        e.total_input_value = i.inputs_value;
303
        e.total_output_value = i.outputs_value;
304
        e.total_fee = i.fee;
305
        e.estimated_change = i.estimated_change;
306
        e.estimated_change_address = i.estimated_change_address;
307
308
        for (var j in i.inputs) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
309
            var inIdx = i.inputs[j];
310
            inputs.push({
311
                index: j,
312
                output_hash: inIdx.prev_tx_hash,
313
                output_index: inIdx.sequence,
314
                value: inIdx.prev_value,
315
                address: inIdx.prev_addresses,
316
                type:  e.is_coinbase ? e.is_coinbase : convertBtccomOutputScriptType(inIdx.prev_type),
317
                multisig: inIdx.multisig == undefined ? null : inIdx.multisig,
318
                script_signature: inIdx.script_hex,
319
                script: inIdx.script_asm
320
            });
321
        }
322
323
        for (var j in i.outputs) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable j already seems to be declared on line 308. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
324
            var outIdx = i.outputs[j];
325
            outputs.push({
326
                index: j,
327
                value: outIdx.value,
328
                address: outIdx.addresses,
329
                type: outIdx.type,
330
                multisig: outIdx.multisig == undefined ? null : outIdx.multisig,
331
                script: outIdx.script_asm,
332
                spent_hash: outIdx.spent_by_tx == undefined ? null : outIdx.spent_by_tx,
333
                script_hex: outIdx.script_hex,
334
                spent_index: outIdx.spent_by_tx_position
335
336
            });
337
        }
338
339
        e.inputs = inputs;
340
        e.outputs  = outputs;
341
342
        /*
343
        Extra fields from Btc.com
344
         */
345
346
        e.is_double_spend = i.is_double_spend;
347
        e.is_sw_tx = i.is_sw_tx;
348
        e.weight = i.weight;
349
        e.witness_hash = i.witness_hash;
350
        e.version = i.version;
351
        data.push(e);
352
    });
353
354
    return {
355
        data: data,
356
        current_page: oldData.data.page,
357
        per_page: oldData.data.pagesize,
358
        total: oldData.data.total_count
359
    };
360
};
361
362
BtccomConverter.prototype.convertAddress = function(oldData, firstSeen) {
363
364
    var data = {};
365
    data.address = oldData.data.address;
366
    data.hash160 = getBase58AddressHash160(oldData.data.address, this.network, this.useNewCashAddr).toString("hex");
367
    data.balance = oldData.data.balance;
368
    data.received = oldData.data.received;
369
    data.sent = oldData.data.sent;
370
    data.transactions = oldData.data.tx_count;
371
    data.utxos = oldData.data.unspent_tx_count - 1;
372
    data.unconfirmed_received = oldData.data.unconfirmed_received;
373
    data.unconfirmed_sent = oldData.data.unconfirmed_sent;
374
    data.unconfirmed_transactions = oldData.data.unconfirmed_tx_count;
375
    data.unconfirmed_utxos = oldData.data.unspent_tx_count;
376
    data.first_tx = oldData.data.first_tx;
377
    data.last_tx = oldData.data.last_tx;
378
    data.category = null; //todo is there a replacement? kill this off for now
379
    data.tag = null; //todo is there a replacement? kill this off for now
380
    data.first_seen = firstSeen;
381
    data.last_seen = firstSeen;
382
383
    return {
384
        data: data
385
    };
386
387
}
388
389
BtccomConverter.prototype.convertAddressUnspentOutputs = function(oldData, address) {
390
391
    var data = [];
392
    var script = getAddressScriptInfo(address, this.network, this.useNewCashAddr);
393
    var script_hex = script.toString("hex");
394
    var script_asm = getScriptAsm(script);
395
    var type = getType(script);
396
    oldData.data.list.forEach(function(i) {
397
        data.push({
398
            hash: i.tx_hash,
399
            time: undefined, //todo
400
            confirmations: i.confirmations,
401
            is_coinbase: true, //todo ask Ruben
402
            value: i.value,
403
            index: i.tx_output_n,
404
            address: address,
405
            type: type,
406
            multisig: null,
407
            script: script_asm,
408
            script_hex: script_hex
409
        })
410
411
    });
412
413
    return {
414
        data: data,
415
        current_page: oldData.data.page,
416
        total: oldData.data.total_count
417
    }
418
}
419
420
exports = module.exports = BtccomConverter;
421